package com.java.shop.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.java.common.to.MemberTo;
import com.java.common.to.OrderTo;
import com.java.common.to.WareSkuLockTo;
import com.java.common.utils.PageUtils;
import com.java.common.utils.Query;
import com.java.common.utils.R;
import com.java.shop.order.constant.OrderConstant;
import com.java.shop.order.dao.OrderDao;
import com.java.shop.order.entity.OrderEntity;
import com.java.shop.order.entity.OrderItemEntity;
import com.java.shop.order.entity.PaymentInfoEntity;
import com.java.shop.order.enume.OrderStatusEnum;
import com.java.shop.order.feign.MemberFeignService;
import com.java.shop.order.feign.ProductFeignService;
import com.java.shop.order.feign.ThirdPartyFeignService;
import com.java.shop.order.feign.WareFeignService;
import com.java.shop.order.inteceptor.OrderInteceptor;
import com.java.shop.order.service.OrderItemService;
import com.java.shop.order.service.OrderService;
import com.java.shop.order.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import static com.java.common.constant.MqConstant.*;


@Slf4j
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    public static ThreadLocal<OrderSubmitVo> confirmThread = new ThreadLocal<>();
    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    ThirdPartyFeignService thirdPartyFeignService;

    @Autowired
    OrderItemService orderItemService;

    @Autowired
    WareFeignService wareFeignService;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    ThreadPoolExecutor executor;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }


    /**
     * 获取confirm页面的所有信息
     * @return
     */
    @Override
    public OrderConfirmVo getConfirmInfo() throws ExecutionException, InterruptedException {
        MemberTo memberTo = OrderInteceptor.threadLocal.get();
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        /* 远程查询会员收货地址 */
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            //将主线程的请求放入到这个异步线程中，这样在requestInteceptor就能拿到请求头信息了，从而给新请求赋值
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressVo> address = null;
            try {
                address = memberFeignService.getAddressListByMemberId(memberTo.getId());
            } catch (Exception e) {
                log.error("OrderServiceImpl::OrderConfirmVo远程查询收货地址失败，{}",e.getMessage());
            }
            orderConfirmVo.setAddress(address);
        },executor);

        /* 远程查询选中的购物项 */
        CompletableFuture<Void> itemsFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderItemVo> cartItem = null;
            try {
                cartItem = thirdPartyFeignService.getCartItemList();
            } catch (Exception e) {
                log.error("OrderServiceImpl::OrderConfirmVo远程查询购物项失败，{}",e.getMessage());
            }
            orderConfirmVo.setItems(cartItem);
        },executor);

        /* 获取用户积分 */
        Integer integration = memberTo.getIntegration();
        if(integration != null) orderConfirmVo.setIntegration(integration);

        /* 其他数据的自动计算 */
        // 运费计算（随机值）

        /* 防重令牌 */
        // TODO 防重令牌
        String token = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(OrderConstant.ORDER_COMMIT_TOKEN_PREFIX+memberTo.getId(), token);
        orderConfirmVo.setOrderToken(token);


        CompletableFuture.allOf(addressFuture,itemsFuture).get();
        return orderConfirmVo;
    }

    /**
     *提交订单
     * @param vo
     * @return
     */
    @Transactional
    @Override
    public OrderSubmitRespVo submitOrder(OrderSubmitVo vo) throws Exception{
        confirmThread.set(vo);
        MemberTo memberTo = OrderInteceptor.threadLocal.get();
        OrderSubmitRespVo respVo = new OrderSubmitRespVo();
        /* 下单，创建订单，验令牌，验价格，锁库存 */


        // 1.为保证幂等性，验令牌的获取与删除必须保证原子性
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        String redisTokenKey = OrderConstant.ORDER_COMMIT_TOKEN_PREFIX + memberTo.getId();
        Long execute = redisTemplate.execute(new DefaultRedisScript<Long>(script,Long.class), Arrays.asList(redisTokenKey), vo.getOrderToken());
        if(execute == 1L){
            // 查到并删除成功
            /* 创建订单 */
            OrderCreateVo order = createOrder();

            /* 验价 */
            BigDecimal payAmount = order.getPryPrice();
            if(Math.abs(payAmount.subtract(vo.getPayPrice()).doubleValue()) <= 0.01){
                //验证通过
                //保存订单
                saveOrder(order);

                WareSkuLockTo wareSkuLockTo = new WareSkuLockTo();
                wareSkuLockTo.setOrderSn(order.getOrder().getOrderSn());
                List<WareSkuLockTo.OrderItemTo> collect = order.getOrderItems().stream().map(item -> {
                    WareSkuLockTo.OrderItemTo itemTo = new WareSkuLockTo.OrderItemTo();
                    itemTo.setSkuId(item.getSkuId());
                    itemTo.setNum(item.getSkuQuantity());
                    return itemTo;
                }).collect(Collectors.toList());

                wareSkuLockTo.setLocks(collect);

                /* 远程锁库存 */
                R r = wareFeignService.orderLockStock(wareSkuLockTo);
                if(r.getCode() == 0){
                    /* 锁定成功 */
                    respVo.setOrder(order.getOrder());
                    /* mq通过延时队列30分钟未支付的进行自动关单 */
                    rabbitTemplate.convertAndSend(order_exchangeName,order_toDeadQueueKey,order.getOrder());
                    respVo.setCode(0);
                }else{
                    //锁定失败
                    respVo.setCode(3);
                    throw new RuntimeException("库存锁定失败");
                }
            }else{
                //验价未通过
                respVo.setCode(2);
            }
        }else{
            // 未查到数据
            respVo.setCode(1);
        }
        return respVo;
    }

    /**
     * 操作数据库保存order和orderitem
     * @param order
     */
    private void saveOrder(OrderCreateVo order) {
        this.save(order.getOrder());
        orderItemService.saveBatch(order.getOrderItems());
    }

    /**
     * 创建订单数据
     * @return
     */
    private OrderCreateVo createOrder() {
        OrderCreateVo createVo = new OrderCreateVo();
        OrderSubmitVo orderSubmitVo = confirmThread.get();
        MemberTo memberTo = OrderInteceptor.threadLocal.get();

        // 创建订单
        OrderEntity orderEntity = new OrderEntity();
        // 1.生成订单号
        orderEntity.setOrderSn(IdWorker.getTimeId());
        orderEntity.setMemberId(memberTo.getId());

        // 2.远程查询收货地址信息
        R r = memberFeignService.info(orderSubmitVo.getAddrId());
        MemberAddressVo addrVo = null;
        if(r.getCode() == 0){
            addrVo = JSON.parseObject(r.getData("memberReceiveAddress"), MemberAddressVo.class);
        }
        if(addrVo != null){
            orderEntity.setReceiverName(addrVo.getName());
            orderEntity.setReceiverCity(addrVo.getCity());
            orderEntity.setReceiverDetailAddress(addrVo.getDetailAddress());
            orderEntity.setReceiverPhone(addrVo.getPhone());
            orderEntity.setReceiverPostCode(addrVo.getPostCode());
            orderEntity.setReceiverProvince(addrVo.getProvince());
            orderEntity.setReceiverRegion(addrVo.getRegion());
        }

        // 3.设置所有的购物项
        List<OrderItemVo> cartItemList = thirdPartyFeignService.getCartItemList();
        List<OrderItemEntity> orderItemList = buildOrderItem(cartItemList,orderEntity.getOrderSn());


        // 4.设置所有价格及价格计算
        orderEntity.setFreightAmount(orderSubmitVo.getFare());
        computePrice(orderEntity,orderItemList);

        // 5.其他属性设置
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setAutoConfirmDay(7);

        // 6.将数据封装到vo
        createVo.setOrder(orderEntity);
        createVo.setOrderItems(orderItemList);
        createVo.setPryPrice(orderEntity.getPayAmount());

        return createVo;
    }

    /**
     * 设置订单的计算属性
     * @param orderEntity
     * @param orderItemList
     */
    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemList) {
        BigDecimal total = new BigDecimal("0");
        BigDecimal promotion = new BigDecimal("0");
        BigDecimal integrationAmount = new BigDecimal("0");
        BigDecimal coupon = new BigDecimal("0");
        BigDecimal integration = new BigDecimal("0");
        Integer growth = 0;
        for (OrderItemEntity orderItemEntity : orderItemList) {
            BigDecimal skuCount = new BigDecimal(orderItemEntity.getSkuQuantity());
            total = total.add(orderItemEntity.getRealAmount().multiply(skuCount));
            if(orderEntity.getPromotionAmount() != null) promotion = promotion.add(orderEntity.getPromotionAmount().multiply(skuCount));
            if(orderEntity.getIntegrationAmount() != null) integrationAmount = integrationAmount.add(orderEntity.getIntegrationAmount().multiply(skuCount));
            if(orderEntity.getCouponAmount() != null) coupon = coupon.add(orderEntity.getCouponAmount().multiply(skuCount));
            if(orderEntity.getIntegration() != null) integration = integration.add(skuCount.multiply(new BigDecimal(orderEntity.getIntegration())));
            if(orderEntity.getGrowth() != null && orderItemEntity.getSkuQuantity() != null) growth += (orderEntity.getGrowth() * orderItemEntity.getSkuQuantity());
        }
        //订单总额
        orderEntity.setTotalAmount(total);
        //应付总额
        orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
        //促销优化金额
        orderEntity.setPromotionAmount(promotion);
        //积分抵扣金额
        orderEntity.setIntegrationAmount(integrationAmount);
        //优惠券抵扣金额
        orderEntity.setCouponAmount(coupon);
        //可以获得的积分
        orderEntity.setIntegration(integration.intValue());
        //可以获得的成长值
        orderEntity.setGrowth(growth);
    }

    /**
     * 创建订单项数据
     * @param list
     * @return
     */
    private List<OrderItemEntity> buildOrderItem(List<OrderItemVo> list,String ordreSn) {
        if(list == null || list.size() == 0 ) return null;
        List<Long> skuIds = list.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
        Map<Long, SpuInfoVo> spuInfo = productFeignService.getSpuInfo(skuIds);
        if(spuInfo == null || spuInfo.size() == 0) return null;
        return list.stream().map(item->{
            // 封装OrderItemEntity
            OrderItemEntity entity = new OrderItemEntity();
            // 设置订单号
            entity.setOrderSn(ordreSn);
            // 设置spu信息
            SpuInfoVo spuInfoVo = spuInfo.get(item.getSkuId());
            entity.setSpuId(spuInfoVo.getId());
            if(spuInfoVo.getSpuName() != null) entity.setSpuName(spuInfoVo.getSpuName());
            else entity.setSpuName(spuInfoVo.getBrandId().toString());
            entity.setSpuBrand(spuInfoVo.getBrandNmae());
            entity.setCategoryId(spuInfoVo.getCatalogId());

            // 设置商品信息
            entity.setSkuId(item.getSkuId());
            entity.setSkuName(item.getTitle());
            entity.setSkuAttrsVals(StringUtils.join(item.getSkuAttr(),";"));
            entity.setSkuPic(item.getImg());
            entity.setSkuPrice(item.getPrice());
            entity.setSkuQuantity(item.getCount());

            //设置优惠信息（不做）
            entity.setPromotionAmount(new BigDecimal("0"));
            entity.setCouponAmount(new BigDecimal("0"));
            entity.setIntegrationAmount(new BigDecimal("0"));
            BigDecimal totalPrice = item.getPrice();
            BigDecimal fianlPrice = totalPrice.subtract(entity.getPromotionAmount())
                    .subtract(entity.getCouponAmount())
                    .subtract(entity.getIntegrationAmount());
            entity.setRealAmount(fianlPrice);

            //设置赠送信息
            entity.setGiftIntegration(item.getPrice().intValue());
            entity.setGiftGrowth(item.getPrice().intValue());

            return entity;
        }).collect(Collectors.toList());
    }

    /**
     * 根据订单号获取订单信息
     * @param orderSn
     * @return
     */
    @Override
    public OrderEntity getInfoByOrderSn(String orderSn) {
        return this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn",orderSn));
    }

    /**
     * 关闭订单操作
     * @param orderEntity
     * @throws Exception
     */
    @Override
    public void orderClose(OrderEntity orderEntity) throws Exception {
       if(orderEntity != null && orderEntity.getStatus() == OrderStatusEnum.CREATE_NEW.getCode()){
           OrderEntity order = new OrderEntity();
           order.setId(orderEntity.getId());
           order.setStatus(OrderStatusEnum.CANCLED.getCode());
           this.updateById(order);
           //给mq发送一个
           OrderTo orderTo = new OrderTo();
           BeanUtils.copyProperties(orderEntity,orderTo);
           rabbitTemplate.convertAndSend(order_exchangeName,order_toWareOrdRoutingKey,orderTo);
       }
    }

    /**
     * 创建支付信息
     * @param orderSn
     * @return
     */
    @Override
    public PayOrderVo getPayOrder(String orderSn) {
        OrderEntity orderEntity = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        PayOrderVo payOrderVo = new PayOrderVo();
        payOrderVo.setOutTradeNo(orderEntity.getOrderSn());
        String totalStr = orderEntity.getPayAmount().setScale(2, BigDecimal.ROUND_UP).toString();
        payOrderVo.setTotalAmount(totalStr);
        List<OrderItemEntity> orderItemList = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
        if(orderItemList.size() > 0) payOrderVo.setSubject(orderItemList.get(0).getSkuName());
        return payOrderVo;
    }

    /**
     * 获取订单所有信息展示到页面
     * @param params
     * @return
     */
    @Override
    public PageUtils queryPageWithItem(Map<String, Object> params) {
        MemberTo memberTo = OrderInteceptor.threadLocal.get();
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>().eq("member_id",memberTo.getId()).orderByDesc("id")
        );

        List<OrderEntity> list = page.getRecords().stream().map(item -> {
            List<OrderItemEntity> items = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", item.getOrderSn()));
            item.setItems(items);
            return item;
        }).collect(Collectors.toList());

        page.setRecords(list);
        return new PageUtils(page);
    }

    /**
     * 支付宝回调参数封装
     * @param vo
     * @return
     */
    @Override
    public String handlePayResult(PayAsyncVo vo) throws Exception {
        if(vo == null) {
            log.error("OrderServiceImpl::handlePayResult【接收支付宝回调失败】");
            return "error";
        }
        /* 保存流水数据 */
        PaymentInfoEntity payment = new PaymentInfoEntity();
        payment.setOrderSn(vo.getOut_trade_no());
        payment.setAlipayTradeNo(vo.getTrade_no());
        payment.setTotalAmount(vo.getTotal_amount());
        payment.setSubject(vo.getSubject());
        payment.setPaymentStatus(vo.getTrade_status());
        payment.setCreateTime(vo.getGmt_create());
        payment.setConfirmTime(vo.getGmt_payment());
        payment.setCallbackContent(JSON.toJSONString(vo));
        payment.setCallbackTime(vo.getNotify_time());

        /* 修改订单交易状态 */
        if("TRADE_SUCCESS".equals(vo.getTrade_status()) || "TRADE_FINISHED".equals(vo.getTrade_status())){
            updateOrderStatus(vo.getOut_trade_no());
        }
        log.info("已收到支付宝通知，返回success");
        return "success";
    }


    public void updateOrderStatus(String orderSn){
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setStatus(OrderStatusEnum.PAY.getCode());
        update(orderEntity,new QueryWrapper<OrderEntity>().eq("order_sn",orderSn));
    }
}